package com.gengzp.rpc.strategy.impl;

import com.fasterxml.jackson.core.type.TypeReference;
import com.gengzp.rpc.core.RpcCacheCoreFunc;
import com.gengzp.rpc.enums.RpcServiceRegisterIpTypeEnum;
import com.gengzp.rpc.exception.RpcException;
import com.gengzp.rpc.model.serviceRegistration.dto.ServiceRegistrationDto;
import com.gengzp.rpc.strategy.RpcRegisterCenterStrategy;
import com.gengzp.rpc.tools.RpcEnvironmentVariableReader;
import com.gengzp.rpc.tools.RpcHttpRequester;
import com.gengzp.rpc.tools.RpcJsonConvertor;
import jakarta.annotation.PreDestroy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import static com.gengzp.rpc.constants.RpcCacheKeyConstants.SERVICE_REGISTER_CACHE_KEY;
import static com.gengzp.rpc.constants.RpcHttpRequestUrlConstants.HTTPS_REQUEST_URL_PREFIX;
import static com.gengzp.rpc.constants.RpcHttpRequestUrlConstants.OUTER_IP_QUERY_URL;
import static com.gengzp.rpc.constants.RpcRedisRegisterCenterConstants.*;

/**
 * @ClassName RpcRegisterCenterStrategyImpl
 * @Description rpc框架注册中心策略实现类
 * @Author gengzp
 * @Date 2025/8/26 10:26
 */
@Component
public class RpcRegisterCenterRedisStrategyImpl implements RpcRegisterCenterStrategy {

    private static final Logger logger = LoggerFactory.getLogger(RpcRegisterCenterRedisStrategyImpl.class);

    @Autowired
    private RpcCacheCoreFunc rpcCacheCoreFunc;

    /**
     * 当前服务的ip
     */
    private String currentServerIp;

    /**
     * 当前服务的注册信息缓存key
     */
    private String serviceRegistrationCacheKey;

    /**
     * 项目是否初始启动
     */
    private Boolean isInit = true;

    /**
     * 定时任务执行器，用于管理心跳任务
     */
    private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(
            r -> new Thread(r, REDIS_REGISTER_CENTER_THREAD_NAME)
    );

    @Override
    public void registerService() {
        // 1.获取当前服务的ip地址
        currentServerIp = getCurrentServerIp();

        // 2.组装redis中的缓存key
        serviceRegistrationCacheKey = String.format("%s%s",
                SERVICE_REGISTER_CACHE_KEY, RpcEnvironmentVariableReader.getCurrentServiceName());

        // 3.使用 ScheduledExecutorService 调度心跳任务
        scheduler.scheduleAtFixedRate(
                () -> {
                    try {
                        processServiceRegistrationCache(serviceRegistrationCacheKey);
                        if (isInit) {
                            isInit = false;
                            logger.info("当前使用 Redis 注册中心, 服务实例信息注册成功 [{}] - {}:{}",
                                    RpcEnvironmentVariableReader.getCurrentServiceName(),
                                    currentServerIp,
                                    RpcEnvironmentVariableReader.getPort());
                        }
                    } catch (Exception e) {
                        logger.error("向 Redis 注册中心发送心跳检测失败", e);
                    }
                },
                FIRST_REGISTER_DELAY_TIME, // 初始延迟时间（首次执行前的等待时间）
                HEARTBEAT_CHECK_INTERVAL_TIME, // 任务执行间隔
                TimeUnit.SECONDS // 时间单位
        );
    }

    /**
     * 服务销毁时关闭定时任务线程池，释放资源
     */
    @PreDestroy
    public void destroy() {
        // 1.强制关闭线程池
        if (!scheduler.isShutdown()) {
            scheduler.shutdownNow();
        }

        // 2.删除当前服务的注册信息
        rpcCacheCoreFunc.del(serviceRegistrationCacheKey);
        logger.info("服务关闭, 移除 Redis 注册中心的服务实例信息");
    }

    /**
     * 获取当前服务的ip地址
     *
     * @return ip地址
     */
    private static String getCurrentServerIp() {
        // 注册内网ip, 则直接使用本地ip地址: 127.0.0.1
        if (RpcEnvironmentVariableReader.getRegisterIpType().equals(RpcServiceRegisterIpTypeEnum.INNER)) {
            return "127.0.0.1";
        } else {
            // 注册公网ip地址
            try {
                return RpcHttpRequester.get(HTTPS_REQUEST_URL_PREFIX + OUTER_IP_QUERY_URL,
                        null, null);
            } catch (Exception e) {
                throw RpcException.get("获取服务器IP失败, 获取ip的接口: " + HTTPS_REQUEST_URL_PREFIX + OUTER_IP_QUERY_URL);
            }
        }
    }

    /**
     * 处理服务注册到redis缓存的逻辑
     *
     * @param registrationCacheKey 服务注册的缓存key
     */
    private void processServiceRegistrationCache(String registrationCacheKey) {
        // 当前系统时间
        Date currentTime = new Date();

        // 取出已存在的服务注册缓存信息
        List<ServiceRegistrationDto> existRegistrationCaches = new ArrayList<>();
        String existRegistrationCacheStr = rpcCacheCoreFunc.get(registrationCacheKey);
        if (existRegistrationCacheStr != null && !existRegistrationCacheStr.isBlank()) {
            existRegistrationCaches.addAll(RpcJsonConvertor
                    .convertJsonToGenericObject(existRegistrationCacheStr,
                            new TypeReference<List<ServiceRegistrationDto>>() {
                            })
                    .stream().filter(v -> !v.getExpireTime().before(currentTime) && !v.getIp().equals(currentServerIp))
                    .toList());
        }

        // 组装本次注册的服务信息
        ServiceRegistrationDto serviceRegistrationDto = new ServiceRegistrationDto();
        serviceRegistrationDto.setServiceName(RpcEnvironmentVariableReader.getCurrentServiceName());
        serviceRegistrationDto.setIp(currentServerIp);
        serviceRegistrationDto.setPort(String.valueOf(RpcEnvironmentVariableReader.getPort()));
        serviceRegistrationDto.setRegisterIpType(RpcEnvironmentVariableReader.getRegisterIpType().getName());
        serviceRegistrationDto.setRegisterTime(new Date());
        serviceRegistrationDto.setExpireTime(
                new Date(serviceRegistrationDto.getRegisterTime().getTime() + SERVER_INSTANCE_INFO_CACHE_TIME * 1000L));
        existRegistrationCaches.add(serviceRegistrationDto);

        // 保存服务地址到缓存中
        rpcCacheCoreFunc.set(registrationCacheKey, RpcJsonConvertor.convertObjectToJson(existRegistrationCaches),
                SERVER_INSTANCE_INFO_CACHE_TIME, TimeUnit.SECONDS);
    }

}
